﻿using System;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Iot.Shared.Path;
using Microsoft.Extensions.Logging;

namespace Iot.TimingDatabase.EnumTypeCache;

public abstract class EnumTypeCacheBase(ILogger<EnumTypeCacheBase> logger) : IEnumTypeCache
{
    // 定义一个ConcurrentDictionary类型的DicTypes属性，用于存储DevicePath和Type的键值对
    private ConcurrentDictionary<DevicePath, Type> DicTypes { get; } = [];


    /// <summary>
    ///     设置DevicePath对应的Type
    /// </summary>
    /// <param name="keyPath"></param>
    /// <param name="enum"></param>
    public void SetType(DevicePath keyPath, Enum @enum)
    {
        var type = @enum.GetType();
        // 如果DicTypes中已经存在keyPath对应的Type，并且与传入的type相同，则直接返回
        if (DicTypes.TryGetValue(keyPath, out var valueType) && valueType == type)
        {
            logger.LogTrace(
                "函数:{函数},keyPath:{keyPath},type:{type},DicTypes中已经存在keyPath对应的Type，并且与传入的type相同，直接返回.",
                nameof(SetType), keyPath, type);
            return;
        }

        // 获取type的全名和程序集的全名
        var fullName = type.FullName;
        var assemblyName = type.Assembly.FullName;
        logger.LogDebug("函数:{函数},keyPath:{keyPath},type:{type},获取type的全名{fullName}和程序集的全名{assemblyName}.",
            nameof(SetType), keyPath, type, fullName, assemblyName);

        // 如果type的全名或程序集的全名为空，则直接返回
        if (fullName is null || assemblyName is null)
        {
            logger.LogDebug(
                "函数:{函数},keyPath:{keyPath},type:{type},type的全名{fullName}或程序集的全名{assemblyName}为空，直接返回.", nameof(SetType),
                keyPath, type, fullName, assemblyName);
            return;
        }

        // 如果能从缓存中获取到keyPath对应的EnumTypeCacheModel，并且与传入的type的全名和程序集的全名相同，则将type添加到DicTypes中
        if (GetEnumTypeCacheModel(keyPath, out var assembly, out var typeName) && assembly == assemblyName &&
            typeName == fullName)
        {
            DicTypes.TryAdd(keyPath, type);
            logger.LogDebug(
                "函数:{函数},keyPath:{keyPath},type:{type},能从缓存中获取到keyPath对应的EnumTypeCacheModel，并且与传入的type的全名和程序集的全名相同，将type添加到DicTypes中.",
                nameof(SetType), keyPath, type);
            return;
        }


        // 如果能将EnumTypeCacheModel添加到缓存中，则将type添加到DicTypes中
        if (!SetEnumTypeCacheModel(keyPath, assemblyName, fullName)) return;
        DicTypes.TryAdd(keyPath, type);
        logger.LogDebug("函数:{函数},keyPath:{keyPath},type:{type},能将EnumTypeCacheModel添加到缓存中，将type添加到DicTypes中.",
            nameof(SetType), keyPath, type);
    }

    /// <summary>
    ///     尝试获取DevicePath对应的Type
    /// </summary>
    /// <param name="keyPath"></param>
    /// <param name="valueType"></param>
    /// <returns></returns>
    public bool TryGetType(DevicePath keyPath, [NotNullWhen(true)] out Type? valueType)
    {
        // 如果DicTypes中存在keyPath对应的Type，则直接返回true
        if (DicTypes.TryGetValue(keyPath, out valueType))
        {
            logger.LogTrace("函数:{函数},keyPath:{keyPath},DicTypes中存在keyPath对应的Type，直接返回true.", nameof(TryGetType),
                keyPath);
            return true;
        }

        // 如果不能从缓存中获取到keyPath对应的EnumTypeCacheModel，则返回false
        if (!GetEnumTypeCacheModel(keyPath, out var assemblyName, out var typeName))
        {
            logger.LogDebug("函数:{函数},keyPath:{keyPath},不能从缓存中获取到keyPath对应的EnumTypeCacheModel，返回false.",
                nameof(TryGetType), keyPath);
            return false;
        }

        valueType = DicTypes.Values.FirstOrDefault(a => a.FullName == typeName && a.Assembly.FullName == assemblyName);
        if (valueType is null)
        {
            // 根据EnumTypeCacheModel中的程序集全名加载程序集
            var assembly = Assembly.Load(assemblyName);
            // 根据EnumTypeCacheModel中的Type全名获取Type
            valueType = assembly.GetType(typeName);
            // 如果获取到的Type为空，则返回false
            if (valueType is null)
            {
                logger.LogDebug("函数:{函数},keyPath:{keyPath},获取到的Type为空，返回false.", nameof(TryGetType), keyPath);
                return false;
            }
        }

        // 将获取到的Type添加到DicTypes中
        DicTypes.TryAdd(keyPath, valueType);
        logger.LogDebug("函数:{函数},keyPath:{keyPath},将获取到的Type添加到DicTypes中，返回true.", nameof(TryGetType), keyPath);
        return true;
    }

    /// <summary>
    ///     抽象方法，用于将EnumTypeCacheModel添加到缓存中
    /// </summary>
    /// <param name="keyPath">唯一路径</param>
    /// <param name="assemblyFullName">程序集全名</param>
    /// <param name="typeFullName">Type全名</param>
    /// <returns></returns>
    protected abstract bool SetEnumTypeCacheModel(DevicePath keyPath, string assemblyFullName, string typeFullName);


    /// <summary>
    ///     抽象方法，用于从缓存中获取EnumTypeCacheModel
    /// </summary>
    /// <param name="keyPath">唯一路径</param>
    /// <param name="assemblyFullName">程序集全名</param>
    /// <param name="typeFullName">Type全名</param>
    /// <returns></returns>
    protected abstract bool
        GetEnumTypeCacheModel(DevicePath keyPath, [NotNullWhen(true)] out string? assemblyFullName,
            [NotNullWhen(true)] out string? typeFullName);
}